/**
 *  Copyright Matt Silbernagel 2010
 *  
 *	This file is part of MobilePrinting.
 *
 *	MobilePrinting is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  MobilePrinting is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with MobilePrinting.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.uc.labs.MobilePrinting.OSUtils;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.NoRouteToHostException;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;

import edu.uc.labs.MobilePrinting.Configurator;
import edu.uc.labs.MobilePrinting.Fetch.XMLFetcher;
import edu.uc.labs.MobilePrinting.GUI.SysTray;
import edu.uc.labs.MobilePrinting.PrintUtils.Printer;
import edu.uc.labs.MobilePrinting.PrintUtils.PrinterFinder;
import edu.uc.labs.MobilePrinting.PrintUtils.PrinterWriter;

import static edu.uc.labs.MobilePrinting.Configurator.*;

/**
 * 
 * @author Matthew Silbernagel This Class sets up the Mobile Print Service and
 *         provides the method for running the service
 * 
 */
public class NetworkManager extends Thread{

	private String ip = null;
	final private String IP = "127.0.0.1";
	private Logger logger = LOGGER;
	private XMLFetcher s;
	public static boolean firstRun = true;
	private static SysTray sys;
	
	
	/**
	 * Constructor for IPAddressFinder
	 */
	public NetworkManager(){
		// Get an local IP Address
		// and get the hostname based on that
		ip = "127.0.0.1";
		
	}
	
	@SuppressWarnings("deprecation")
	public static void exit(){
		sys.stop();
	}
	
	/**
	 * 
	 */
	public void run() {
		while (true){
			String ipCheck = getIP();
			LOGGER.debug("ipCheck = " + ipCheck);
			LOGGER.debug("ip = " + this.ip);
			if(!ipCheck.equalsIgnoreCase(this.ip) || firstRun ){
				LOGGER.debug("IPAddressFinder: The ip has changed from " + this.ip + " to " + ipCheck );
				ip = ipCheck;
				// Now get a new XML file for the current IP
				if(checkConnection(ip)){
					writeConfiguration();
					LOGGER.debug("Created the xml file");
				} else {
					try {
						LOGGER.debug("Writing a blank Configuration File");
						PrinterWriter writer;
						writer = new PrinterWriter();
						writer.writeBlank();
					} catch (ParserConfigurationException e) {
						LOGGER.error("Error while writing a blank XML File");
					}
				}
				if(!firstRun){
					
					ArrayList<Printer> list = PrinterFinder.getList();
					sys.add(list);
					
				}
			} else {
				LOGGER.debug("IPAddressFinder: The ip has not changed, ipCheck = " + ipCheck + ", ip = " + ip);
			}
			startSysTrayThread();
			try {
				Thread.sleep(SLEEP);
			} catch (InterruptedException e) {
				LOGGER.error(e.toString());
			}
		}
	}

	/*
	 * Method that returns a string representation of the localhost's IP Address
	 * @return string ipaddress
	 */
	public String getIP() {
		LOGGER.debug("Checking for a new IP Address");
		try {
			Enumeration<NetworkInterface> e = NetworkInterface
					.getNetworkInterfaces();
			while (e.hasMoreElements()) {
				NetworkInterface ni = (NetworkInterface) e.nextElement();
				String displayName = ni.getDisplayName();
				Enumeration<InetAddress> e2 = ni.getInetAddresses();
				while (e2.hasMoreElements()) {
					InetAddress inet = (InetAddress) e2.nextElement();
					String stringIP = cleanup(inet.toString(), "/", false);
					LOGGER.debug("The ipaddress i found for " + displayName + " is: " + stringIP);
					// Check if we have a IPv4 formatted address
					
					if (stringIP.matches("^\\d+\\.\\d+\\.\\d+\\.\\d+$")) {
						if(displayName.startsWith("vmnet")){ continue; }
						LOGGER.debug(stringIP + " is well formed");
						// We seem to have a well formed IP Address
						// If this is the default localhost address skip this
						if(stringIP.equals("127.0.0.1")){ continue;}
						LOGGER.debug("Returning " + stringIP);
						return stringIP;
					}
				}
			}
		} catch (Exception e) {
			logger.error("IPAddressFinder: Can not get IP Address " + e.toString()
					+ " returning the default value");
			return IP;
		}
		
		LOGGER.debug("Returning " + IP);
		return IP;

	}

	/**
	 * Setter for the IP Address
	 * 
	 * @param addr
	 *            The address to set
	 */
	public boolean setIP(String addr) {
		this.ip = addr;
		if (this.ip.equals(addr)) {
			return true;
		}
		return false;
	}

	/**
	 * Method that compares two IP Addresses
	 */
	public boolean isEqual(String otherIP) {
		if (ip.equals(otherIP)) {
			return true;
		}
		return false;
	}

	/**
	 * Method that determines if the IP passed in is the same as the current
	 * object holds
	 * 
	 * @param myNewIP
	 *            the ip to compare against
	 * @return true if the IP's are different
	 */
	public boolean hasChanged(String myNewIP) {
		if (this.ip.equals(myNewIP)) {
			logger.debug("Your IP has not changed ");
			// if the two addresses are equal
			// then there is nothing to do
			return false;
		} else {
			logger.debug("Your IP has changed");
			// otherwise there is a change
			// in the IP address
			// set the new IP Address
			this.ip = myNewIP;
			return true;
		}
	}

	/**
	 * @return a string representation of the current IP address
	 */
	public String toString() {
		return ip;
	}

	/**
	 * Clean characters from a string
	 * 
	 * @param s
	 *            String to clean
	 * @param sToMatch
	 *            character to clean
	 * @param isToKeep
	 * @return
	 */
	private String cleanup(String s, String sToMatch, boolean isToKeep) {
		final int size = s.length();
		StringBuffer buf = new StringBuffer(size);
		if (!isToKeep) {
			for (int i = 0; i < size; i++) {
				if (sToMatch.indexOf(s.charAt(i)) == -1) {
					buf.append(s.charAt(i));
				}
			}
		} else {
			for (int i = 0; i < size; i++) {
				if (sToMatch.indexOf(s.charAt(i)) != -1) {
					buf.append(s.charAt(i));
				}
			}
		}
		return buf.toString();

	}

	public boolean checkConnection(String ipaddress){
		while (true) {
			try {
				s = new XMLFetcher(Configurator.HTTP_SERVER, ipaddress, URLEncoder.encode(Configurator.OS, "UTF-8") );
				// Good we can connect to the server
				// now break out of this loop
				return true;
			} catch (UnknownHostException e) {
				// Are we on the UC Network?
				// Set the printer to Paused?
				logger.error("Could not find any printers");
				return false;
			} catch (ConnectException e) {
				// Are we on the UC Network?
				// Set the printer to Paused?
				logger.info("You do not appear to be on campus. There are no printers available");
				return false;
			} catch (NullPointerException e) {
				logger
						.debug("Caught a NullPointerException in the checkConnection method");
			} catch (NoRouteToHostException e) {
				logger.debug("Caught a NoRouteToHostException");
			} catch (IOException e){
				logger.error(e.toString());
				return false;
			}

		}
	}
	
	
	public void writeConfiguration() {
		try {
			String file_name = Configurator.USERSPACE + Configurator.XMLFILE;
			FileWriter fstream = new FileWriter(file_name);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write(s.getResponse());
			out.close();
		} catch (IOException ex) {
			LOGGER.error(ex.getMessage());
		}
	}
	
	
	private void startSysTrayThread(){
		if (firstRun){
			sys = new SysTray();
			sys.start();
			firstRun = false;
		}
	}
	

}
